package com.zhou.cloud.service.impl;

import cn.hutool.core.date.DateUtil;
import com.zhou.cloud.service.IDistributedLockService;
import lombok.extern.slf4j.Slf4j;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

/**
 * @descrition: redis实现分布式锁接口
 * @author: zhouyongjun
 * @date: 2022/11/1
 **/
@Service("zookeeperDistributedLockService")
@Slf4j
public class ZookeeperDistributedLockServiceImpl implements IDistributedLockService {
    @Autowired
    private CuratorFramework client;
        //利用ThreadLocal 存储当前线程锁数据
    ThreadLocal<InterProcessMutex> threadLocal = new ThreadLocal<>();
    /**
     事件监听：通过zk的事件监听机制可以让客户端收到节点状态变化。主要的事件类型有节点数据变化、节点的删除和创建。
     PERSISTENT：持久化的节点。一旦创建后，即使客户端与zk断开了连接，该节点依然存在。
     PERSISTENT_SEQUENTIAL：持久化顺序编号节点。比PERSISTENT节点多了节点自动按照顺序编号。
     EPHEMERAL：临时节点。当客户端与zk断开连接之后，该节点就被删除。
     EPHEMERAL_SEQUENTIAL：临时顺序编号节点。比EPHEMERAL节点多了节点自动按照顺序编号。(分布式锁实现使用该节点类型)
     */
    /**
    * @Description:
    * @Params:
     * expireTime : 此处会设置成加锁等待时间
    * @Returns:
    * @Author: zhouyongjun
    * @Date Created in 2022/11/2 9:04
     */
    @Override
    public boolean tryLock(String lockKey, String requestId, TimeUnit timeUnit, int expireTime) {
        return tryLock(lockKey,requestId,timeUnit,expireTime,expireTime);
    }

    @Override
    public boolean tryLock(String lockKey, String requestId, TimeUnit timeUnit, int expireTime, int waitingTime) {
        long beginTryTime = System.currentTimeMillis();
        log.info("tryLock wait begin lockKey={},requestId={} beginTime={},outTime={}",lockKey,requestId,
                DateUtil.date(beginTryTime).toString("yyyy-MM-dd HH:mm:ss.SSS"),
                DateUtil.date(beginTryTime+waitingTime).toString("yyyy-MM-dd HH:mm:ss.SSS"));
        //可重入
        InterProcessMutex mutex  = threadLocal.get();
        if (mutex != null) {
            releaseLock(lockKey,requestId);
        }
        mutex  = new InterProcessMutex(client,lockKey+requestId);
        //不可重入
//        mutex = new InterProcessSemaphoreMutex(client,reentrantPrefix + key);
        boolean acquire = false;
        try {
            acquire =  mutex.acquire(waitingTime, timeUnit);
//            System.out.println("mutex acquire="+acquire);
            //加锁成功则存储该锁对象
            if (acquire) {
                threadLocal.set(mutex);
            }
        } catch (Exception e) {
            log.error("",e);
        }
        log.info("tryLock wait end lockKey={},requestId={} expireTime={},waitingTime={},outTime={},result={},cost(ms)={},mutex={}",lockKey,requestId,expireTime,waitingTime,
                DateUtil.date(beginTryTime+waitingTime).toString("yyyy-MM-dd HH:mm:ss.SSS"),acquire,System.currentTimeMillis()-beginTryTime,mutex);
        return acquire;
    }

    @Override
    public boolean releaseLock(String lockKey, String requestId) {
        InterProcessMutex mutex  = threadLocal.get();
        boolean isTrue = false;
        if (mutex != null) {
            try {
                mutex.release();
                isTrue = true;
            } catch (Exception e) {
                log.error("",e);
            }
            threadLocal.remove();
        }
        log.info("releaseLock lockKey={},requestId={},result={},mutex={}",lockKey,requestId,isTrue,mutex);
        return false;
    }
}
